﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Gumini.JulaDB.Enums;
using Gumini.JulaDB.Provider;

namespace Gumini.JulaDB.Expressions.Operators
{
    internal static class OperatorFactory
    {
        private static readonly Dictionary<Tuple<string, int>, Operator> _operators = new Dictionary<Tuple<string, int>, Operator>();

        static OperatorFactory()
        {
            var functionTypes = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsSubclassOf(typeof(Operator)) && !t.IsAbstract);
            foreach(var type in functionTypes)
                RegisterOperator((Operator)Activator.CreateInstance(type));
        }

        private static void RegisterOperator(Operator op)
        {
            foreach (var name in op.Names)
            {
                var id = Tuple.Create(name, op.NumberOfArguments);
                if (_operators.ContainsKey(id))
                    throw new InvalidOperationException(String.Format("{0}: there is already an operator '{1}' ({2}).", op.GetType(), id.Item1, id.Item2));
                _operators.Add(id, op);
            }
        }

        public static UnaryOperator GetUnaryOperator(string name)
        {
            return (UnaryOperator)GetOperator(name, 1);
        }

        public static BinaryOperator GetBinaryOperator(string name)
        {
            return (BinaryOperator)GetOperator(name, 2);
        }

        private static Operator GetOperator(string name, int numberOfArguments)
        {
            var id = Tuple.Create(name.ToLowerInvariant(), numberOfArguments);
            if (!_operators.ContainsKey(id))
                throw new JulaDbException(ErrorCode.UnsupportedOperator,
                    "{0} operator '{1}' is not supported.", numberOfArguments == 1 ? "Unary" : "Binary", name);
            return _operators[id];
        }
    }
}
